home *** CD-ROM | disk | FTP | other *** search
/ Programming Languages Suite / ProgramD2.iso / Borland / Borland C++ V5.02 / GDIDIB.PAK / CLIENT.C < prev    next >
C/C++ Source or Header  |  1997-05-06  |  43KB  |  1,514 lines

  1. // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
  2. // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
  3. // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
  4. // PARTICULAR PURPOSE.
  5. //
  6. // Copyright (C) 1993 - 1995  Microsoft Corporation.  All Rights Reserved.
  7. //
  8. //  MODULE: client.c
  9. //
  10. //  PURPOSE: Handles general routines for the GDI Input sample.
  11. //
  12. //  FUNCTIONS:
  13. //    CreateClientWindow  - Creates the client window that handles input.
  14. //    ClientWndProc       - Processes messages for the client window.
  15. //    CmdDrawMode         - Changes the current drawing mode.
  16. //    CmdFill             - Toggles the fill mode.
  17. //    CmdCreatePen        - Puts up Pen Style dialog box.
  18. //    CmdCreateBrush      - Puts up Brush Style dialog box.  
  19. //    CmdClear            - Forces client window to repaint.
  20. //    MsgClientCreate     - Creates initial pen and brush objects.  
  21. //    InitDrawObjects     - Initializes the pen and brush used for drawing.
  22. //    MsgClientDestroy    - Frees owned objects.
  23. //    MsgClientKeyDown    - Looks for Escape key to cancel drawing.
  24. //
  25. //    MsgClientLButtonDown
  26. //    MsgClientMouseMove => Pass mouse messages to the current handlers.
  27. //    MsgClientLButtonUp
  28. //
  29. //    PixelLBDown
  30. //    PixelMouseMove    ==> Draw individual pixels with SetPixelV.
  31. //    PixelLBUp
  32. //
  33. //    BezierLBDown
  34. //    BezierMouseMove   ==> Select 4 points for drawing a Bezier curve.
  35. //    BezierLBUp
  36. //
  37. //    RectLBDown
  38. //    RectMouseMove     ==> Select 2 points for a line, rect, or ellipse.
  39. //    RectLBUp
  40. //
  41. //    LineDraw              Given 2 points, draw a line, rect, or ellipse.
  42. //    RectDraw          ==> These functions all take identical parameters
  43. //    EllipseDraw           so they can be used interchangeably.
  44. //
  45. //    StartRubberBand     - Initiates drawing procedure.
  46. //    EndRubberBand       - Ends drawing procedure.
  47. //
  48. //    SizeClientWindow    - Creates scrollbars if client can't display entire bitmap.                                                         
  49. //    MsgClientPaint      - Paints the client window in response to WM_PAINT message.
  50. //    MsgClientScroll     - Scrolls the client window in response to scroll message.
  51. //
  52. //  COMMENTS:
  53. //
  54.  
  55. #include <windows.h>            // required for all Windows applications
  56. #include <windowsx.h>
  57. #include  <commctrl.h>          // prototypes and structure for the common controls.
  58.  
  59. #include "globals.h"            // prototypes specific to this application
  60. #include "resource.h"
  61. #include "toolbar.h"            // prototypes for the tool bar
  62. #include "statbar.h"            // prototypes for the status bar
  63. #include "pendlg.h"             // ID's and prototypes for the Pen dialog
  64. #include "brushdlg.h"           // ID's and prototypes for the Brush dialog
  65. #include "dibutil.h"
  66.  
  67. #ifdef SetPixelV  // temporary workaround since SetPixelV isn't working yet
  68. #undef SetPixelV
  69. #endif
  70. #define SetPixelV SetPixel
  71.  
  72. // Global variables local to this file
  73.  
  74. HPEN    hpenBlack;              // Useful stock objects
  75. HBRUSH  hbrNull;
  76.  
  77. HPEN    hpenDraw;               // for drawing lines and frames
  78. HBRUSH  hbrReal;                // for filling interiors
  79. HBRUSH  hbrDraw;                // equals either hbrReal or hbrNull
  80.  
  81. HDC     hdcRB;                  // DC for Rubber Banding
  82. BOOL    bDrawing;               // Flag to indicate when we are drawing
  83. UINT    uDrawMode;              // IDM_LINE, IDM_RECT, etc.
  84.  
  85. #define BEZ_MAXPOINTS   4       // # of points in a Bezier curve
  86. POINT   pPoints[BEZ_MAXPOINTS]; // Stores coordinates for various drawing fns
  87. UINT    cPoints;                // Current # of points
  88.  
  89. // for working with bitmap in memory DC
  90. HDC     hdcMem;
  91. HBITMAP hbmOld;
  92. HPALETTE hpalRB, hpalMem;
  93.  
  94. // for scrolling
  95. int cxHorzScrollPos = 0;
  96. int cyVertScrollPos = 0;
  97.  
  98. // The following 3 function pointers are used to point to the current
  99. // mouse handling functons.  Different drawing types can each define a
  100. // set of mouse functions.  When a new drawing mode is selected, these
  101. // pointers are set to point to the correct functions for that mode.
  102. // The main mouse message handlers then dereference these pointers to
  103. // call the correct handlers.
  104.  
  105. LRESULT  (*pfnLBDown)   (HWND, POINT);      // current mouse handler
  106. LRESULT  (*pfnLBUp)     (HWND, POINT);      // functions for rubber
  107. LRESULT  (*pfnMouseMove)(HWND, POINT);      // banding and such
  108.  
  109.  
  110. // The Line, Rectangle, and Ellipse modes use a common set of mouse
  111. // handlers that select 2 points.  The following function pointer is
  112. // used so that the correct drawing function is always used.  Like the
  113. // mouse function pointers, this is set when the drawing mode is
  114. // selected.
  115.  
  116. BOOL (*pfnDrawRect)(HDC, POINT, POINT);     // current drawing function
  117.  
  118. // Mouse handling functions for different types of drawing.
  119. LRESULT  PixelLBDown(HWND, POINT);          // Pixels
  120. LRESULT  PixelLBUp(HWND, POINT);
  121. LRESULT  PixelMouseMove(HWND, POINT);
  122.  
  123. LRESULT  BezierLBDown(HWND, POINT);         // Bezier Curves
  124. LRESULT  BezierLBUp(HWND, POINT);
  125. LRESULT  BezierMouseMove(HWND, POINT);
  126.  
  127. LRESULT  RectLBDown(HWND, POINT);           // 2-point selection
  128. LRESULT  RectLBUp(HWND, POINT);             // Used for Lines, Rects,
  129. LRESULT  RectMouseMove(HWND, POINT);        // and Ellipses
  130.  
  131. // Generic drawing functions for use with the above Rect functions
  132. BOOL     LineDraw(HDC, POINT, POINT);
  133. BOOL     RectDraw(HDC, POINT, POINT);
  134. BOOL     EllipseDraw(HDC, POINT, POINT);
  135.  
  136. // Other helper functions
  137. void StartRubberBand(HWND);
  138. void EndRubberBand(HWND);
  139. UINT BitmapWidth(HBITMAP);
  140. UINT BitmapHeight(HBITMAP);
  141.  
  142. // Client window message handling functions
  143. static LRESULT MsgClientCreate         (HWND, UINT, WPARAM, LPARAM);
  144. static LRESULT MsgClientDestroy        (HWND, UINT, WPARAM, LPARAM);
  145. static LRESULT MsgClientMouseMove      (HWND, UINT, WPARAM, LPARAM);
  146. static LRESULT MsgClientLButtonDown    (HWND, UINT, WPARAM, LPARAM);
  147. static LRESULT MsgClientLButtonUp      (HWND, UINT, WPARAM, LPARAM);
  148. static LRESULT MsgClientKeyDown        (HWND, UINT, WPARAM, LPARAM);   
  149. static LRESULT MsgClientPaint          (HWND, UINT, WPARAM, LPARAM);
  150. static LRESULT MsgClientScroll         (HWND, UINT, WPARAM, LPARAM);     
  151.  
  152. // Client window message table definitions.
  153. MSD rgmsdClient[] =
  154. {
  155.     {WM_MOUSEMOVE,       MsgClientMouseMove       },
  156.     {WM_LBUTTONDOWN,     MsgClientLButtonDown     },
  157.     {WM_LBUTTONUP,       MsgClientLButtonUp       },
  158.     {WM_KEYDOWN,         MsgClientKeyDown         },
  159.     {WM_CREATE,          MsgClientCreate          },
  160.     {WM_DESTROY,         MsgClientDestroy         },   
  161.     {WM_PAINT,           MsgClientPaint           },
  162.     {WM_HSCROLL,         MsgClientScroll          },
  163.     {WM_VSCROLL,         MsgClientScroll          }
  164. };
  165.  
  166.  
  167. MSDI msdiClient =
  168. {
  169.     sizeof(rgmsdClient) / sizeof(MSD),
  170.     rgmsdClient,
  171.     edwpWindow
  172. };
  173.  
  174.  
  175. //
  176. //  FUNCTION: CreateClientWindow(HWND)
  177. //
  178. //  PURPOSE: Create the client window.
  179. //
  180. //  PARAMETERS:
  181. //    hwndParent - The parent (main) window.
  182. //
  183. //  RETURN VALUE:
  184. //    HWND of client window or null if failure.
  185. //
  186. //  COMMENTS:
  187. //
  188.  
  189. HWND CreateClientWindow(HWND hwndParent)
  190. {
  191.     return CreateWindowEx(0, 
  192.                           "ClientWndClass",
  193.                           NULL,
  194.                           WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS,
  195.                           -100, -100, 10, 10,   // dummy initial coordinates
  196.                           hwndParent, 
  197.                           (HMENU)-1, 
  198.                           hInst, 
  199.                           NULL);
  200. }
  201.  
  202.  
  203. //
  204. //  FUNCTION: ClientWndProc(HWND, UINT, WPARAM, LPARAM)
  205. //
  206. //  PURPOSE:  Processes messages for the client window.
  207. //
  208. //  PARAMETERS:
  209. //    hwnd     - window handle
  210. //    uMessage - message number
  211. //    wparam   - additional information (dependant on message number)
  212. //    lparam   - additional information (dependant on message number)
  213. //
  214. //  RETURN VALUE:
  215. //    The return value depends on the message number.  If the message
  216. //    is implemented in the message dispatch table, the return value is
  217. //    the value returned by the message handling function.  Otherwise,
  218. //    the return value is the value returned by the default window procedure.
  219. //
  220. //  COMMENTS:
  221. //    Call the DispMessage() function with the client window's message
  222. //    dispatch information (msdiClient) and the message specific information.
  223. //
  224.  
  225. LRESULT CALLBACK ClientWndProc(HWND   hwnd, 
  226.                                UINT   uMessage, 
  227.                                WPARAM wparam, 
  228.                                LPARAM lparam)
  229. {
  230.     return DispMessage(&msdiClient, hwnd, uMessage, wparam, lparam);
  231. }
  232.  
  233.  
  234. //
  235. //  FUNCTION: SizeClientWindow(HWND)
  236. //
  237. //  PURPOSE: Resizes the client window when the main window's size has changed or
  238. //           a bitmap is created or loaded.  Attempts to keep as much of the bitmap
  239. //         visible as possible.
  240. //
  241. //  PARAMETERS:
  242. //    hwnd - handle of Client window.
  243. //
  244. //  RETURN VALUE:
  245. //    none.
  246. //
  247. //  COMMENTS:
  248. //
  249.  
  250. void SizeClientWindow(HWND hwnd)    
  251. {
  252.     RECT rc, rcClient, rcFrame; 
  253.     int  cxRange = 0, cyRange = 0;     // Range needed for horz and vert 
  254.     int cxBitmap, cyBitmap;
  255.     
  256.     if (!hBitmap)
  257.     {
  258.         // no bitmap, so the client window is not needed
  259.         MoveWindow(hWndClient, 0, 0, 0, 0, TRUE);
  260.         return;
  261.     }
  262.                            
  263.     cxBitmap = BitmapWidth(hBitmap);
  264.     cyBitmap = BitmapHeight(hBitmap);
  265.                                
  266.     rcClient.left = rcClient.top = 0;
  267.    
  268.     // adjust size of client window to bitmap       
  269.     rcClient.right = cxBitmap;
  270.     rcClient.bottom = cyBitmap;     
  271.     
  272.     // get screen coordinates of toolbar window
  273.     GetWindowRect(hWndToolbar, &rc);
  274.     
  275.     // convert rc.right, rc.bottom to client coordinates
  276.     ScreenToClient(hwnd, ((LPPOINT)&rc) + 1);
  277.     
  278.     // adjust origin of Client window to be flush below Toolbar
  279.     rcClient.top = rc.bottom + 1;
  280.  
  281.     // get screen coordinates of statusbar window
  282.     GetWindowRect(hWndStatusbar, &rc);
  283.     
  284.     // convert rc.left, rc.top to client coordinates
  285.     ScreenToClient(hwnd, (LPPOINT)&rc);
  286.                                         
  287.     // get size of frame window
  288.     GetClientRect(hwnd, &rcFrame);
  289.                                             
  290.     // if client window would overlap statusbar, adjust the client window's size to be
  291.     // flush with the top of the statusbar window instead
  292.     if (rc.top < rcClient.bottom + rcClient.top)
  293.     {
  294.         // rcClient.bottom is the height in the MoveWindow call which follows below
  295.         rcClient.bottom = rc.top - rcClient.top;  
  296.       
  297.         // widen the client window to accomodate a vertical scrollbar    
  298.         rcClient.right += GetSystemMetrics(SM_CXVSCROLL);     
  299.         
  300.         // setup vertical scrollbar range
  301.         cyRange = cyBitmap - rcClient.bottom;      
  302.     }
  303.  
  304.     // adjust width of Client window to fit into frame window
  305.     if (rcClient.right > rcFrame.right)   
  306.     {
  307.         // rcClient.right is the width in the MoveWindow call which follows below        
  308.         rcClient.right = rcFrame.right;
  309.       
  310.         // make room for horizontal scrollbar if possible
  311.         rcClient.bottom = min(rcClient.bottom + GetSystemMetrics(SM_CYHSCROLL), 
  312.                               rc.top - rcClient.top);    
  313.                               
  314.         // setup horizontal scrollbar range
  315.         cxRange = cxBitmap - rcClient.right;
  316.             
  317.         // recalculate vertical scrollbar range
  318.         cyRange = cyBitmap - rcClient.bottom + GetSystemMetrics(SM_CYHSCROLL);            
  319.             
  320.         if (cyRange > 0)                    
  321.             // make allowance for a vertical scrollbar in the horizontal range
  322.             cxRange += GetSystemMetrics(SM_CXVSCROLL);                
  323.     }        
  324.    
  325.     // Set the ranges we've calculated (0 to 0 means invisible scrollbar)
  326.     SetScrollRange(hWndClient, SB_VERT, 0, cyRange, TRUE);
  327.     SetScrollRange(hWndClient, SB_HORZ, 0, cxRange, TRUE); 
  328.     
  329.     // reset global variable if a scrollbar is turned off                               
  330.     if (cyRange == 0)
  331.         cyVertScrollPos = 0;                                    
  332.     if (cxRange == 0)
  333.         cxHorzScrollPos = 0;    
  334.  
  335.     // resize the client window
  336.     MoveWindow(hWndClient,
  337.                rcClient.left,
  338.                rcClient.top,
  339.                rcClient.right,
  340.                rcClient.bottom,
  341.                TRUE);   
  342.            
  343.     InvalidateRect(hWndClient, NULL, FALSE);        
  344. }
  345.  
  346.  
  347. //
  348. //  FUNCTION: CmdDrawMode(HWND, WORD, WORD, HWND)
  349. //
  350. //  PURPOSE: Handles WM_COMMAND messages to change drawing mode.
  351. //
  352. //  PARAMETERS:
  353. //    hwnd - The main window.
  354. //    wCommand - IDM_LINE, IDM_RECT, etc.
  355. //    wNotify  - Notification number (unused)
  356. //    hwndCtrl - NULL (unused)
  357. //
  358. //  RETURN VALUE:
  359. //    Always returns 0 - command handled.
  360. //
  361. //  COMMENTS:
  362. //
  363.        
  364. #pragma argsused
  365. LRESULT CmdDrawMode(HWND hwnd, WORD wCommand, WORD wNotify, HWND hwndCtrl)
  366. {   
  367.     if (wCommand == uDrawMode)  // First see if the mode has changed
  368.         return 0;
  369.  
  370.      // Update the menu and toolbar states
  371.   
  372.     CheckMenuItem(hMenu, uDrawMode, MF_BYCOMMAND | MF_UNCHECKED);
  373.     uDrawMode = wCommand;
  374.     CheckMenuItem(hMenu, uDrawMode, MF_BYCOMMAND | MF_CHECKED);
  375.  
  376.     SendMessage(hWndToolbar, TB_CHECKBUTTON, uDrawMode, MAKELONG(TRUE, 0));
  377.  
  378.     // Set the pointers for the mouse handler functions to
  379.     // the appropriate functions.
  380.  
  381.     switch(uDrawMode)
  382.     {
  383.         case IDM_PIXEL:
  384.             pfnLBDown     = PixelLBDown;
  385.             pfnLBUp       = PixelLBUp;
  386.             pfnMouseMove  = PixelMouseMove;
  387.             break;
  388.  
  389.         case IDM_BEZIER:
  390.             cPoints       = 0;
  391.             pfnLBDown     = BezierLBDown;
  392.             pfnLBUp       = BezierLBUp;
  393.             pfnMouseMove  = BezierMouseMove;
  394.             break;
  395.  
  396.         case IDM_RECT:
  397.             pfnDrawRect   = RectDraw;
  398.             pfnLBDown     = RectLBDown;
  399.             pfnLBUp       = RectLBUp;
  400.             pfnMouseMove  = RectMouseMove;
  401.             break;
  402.  
  403.         case IDM_ELLIPSE:
  404.             pfnDrawRect   = EllipseDraw;
  405.             pfnLBDown     = RectLBDown;
  406.             pfnLBUp       = RectLBUp;
  407.             pfnMouseMove  = RectMouseMove;
  408.             break;
  409.  
  410.         default:
  411.         case IDM_LINE:
  412.             pfnDrawRect   = LineDraw;
  413.             pfnLBDown     = RectLBDown;
  414.             pfnLBUp       = RectLBUp;
  415.             pfnMouseMove  = RectMouseMove;
  416.             break;
  417.     }
  418.  
  419.     return 0;
  420. }
  421.  
  422.  
  423. //
  424. //  FUNCTION: CmdFill(HWND, WORD, WORD, HWND)
  425. //
  426. //  PURPOSE: Handles the IDM_FILL and IDM_NOFILL command messages.
  427. //    Controls whether closed objects (rect's and ellipses) are filled
  428. //    with a brush when drawn.
  429. //
  430. //  PARAMETERS:
  431. //    hwnd     - The main window.
  432. //    wCommand - IDM_FILL or IDM_NOFILL
  433. //    wNotify  - Notification number (unused)
  434. //    hwndCtrl - NULL (unused)
  435. //
  436. //  RETURN VALUE:
  437. //    Always returns 0 - command handled.
  438. //
  439. //  COMMENTS:
  440. //    The toolbar has 2 buttons (IDM_FILL and IDM_NOFILL) but the
  441. //    menu only has IDM_FILL, so this function will turn filling
  442. //    OFF if wCommand is IDM_NOFILL, and TOGGLE the ON/OFF state
  443. //    if wCommand is IDM_FILL.
  444. //
  445.  
  446. #pragma argsused
  447. LRESULT CmdFill(HWND hwnd, WORD wCommand, WORD wNotify, HWND hwndCtrl)
  448. {
  449.     static BOOL bFill;
  450.  
  451.     if (IDM_NOFILL == wCommand)
  452.         bFill = FALSE;                          // turn filling OFF
  453.     else
  454.         bFill = !bFill;                         // toggle fill state
  455.  
  456.     // Update the menu and toolbar states
  457.  
  458.      CheckMenuItem(hMenu,
  459.                   IDM_FILL,
  460.                   MF_BYCOMMAND | (bFill ? MF_CHECKED : MF_UNCHECKED));
  461.  
  462.     SendMessage(hWndToolbar,
  463.                 TB_CHECKBUTTON,
  464.                 (bFill ? IDM_FILL : IDM_NOFILL),
  465.                 MAKELONG(TRUE, 0));
  466.  
  467.     // Update hbrDraw
  468.     hbrDraw = (bFill ? hbrReal : hbrNull);
  469.  
  470.      return 0;
  471. }
  472.  
  473.  
  474. //
  475. //  FUNCTION: CmdCreatePen(HWND, WORD, WORD, HWND)
  476. //
  477. //  PURPOSE: Handles the IDM_CREATEPEN command message. Puts up a dialog
  478. //    to let the user set the pen style to use for drawing.
  479. //
  480. //  PARAMETERS:
  481. //    hwnd     - The main window.
  482. //    wCommand - IDM_CREATEPEN (unused)
  483. //    wNotify  - Notification number (unused)
  484. //    hwndCtrl - NULL (unused)
  485. //
  486. //  RETURN VALUE:
  487. //    Always returns 0 - command handled.
  488. //
  489. //  COMMENTS:
  490. //
  491.  
  492. #pragma argsused
  493. LRESULT CmdCreatePen(HWND hwnd, WORD wCommand, WORD wNotify, HWND hwndCtrl)
  494. {
  495.     LOGPEN lp = logPen;     // copy for the dialog to munge on
  496.  
  497.     if (DialogBoxParam(hInst, "PenDlg", hwnd, (DLGPROC)PenDlg, (LPARAM)&lp))
  498.     {
  499.         DeleteObject(hpenDraw);                     // delete old pen
  500.         logPen = lp;                                // save changes
  501.         hpenDraw = CreatePenIndirect(&logPen);      // create new pen
  502.     }
  503.  
  504.     return 0;
  505. }
  506.  
  507.  
  508. //
  509. //  FUNCTION: CmdCreateBrush(HWND, WORD, WORD, HWND)
  510. //
  511. //  PURPOSE: Handles the IDM_CREATEBRUSH command message. Puts up a dialog
  512. //    to let the user set the brush style to use for drawing.
  513. //
  514. //  PARAMETERS:
  515. //    hwnd - The main window.
  516. //    wCommand - IDM_CREATEBRUSH (unused)
  517. //    wNotify  - Notification number (unused)
  518. //    hwndCtrl - NULL (unused)
  519. //
  520. //  RETURN VALUE:
  521. //    Always returns 0 - command handled.
  522. //
  523. //  COMMENTS:
  524. //
  525.  
  526. #pragma argsused
  527. LRESULT CmdCreateBrush(HWND hwnd, WORD wCommand, WORD wNotify, HWND hwndCtrl)
  528. {
  529.     LOGBRUSH lb = logBrush; // copy for the dialog to munge on
  530.  
  531.     if (DialogBoxParam(hInst, "BrushDlg", hwnd, (DLGPROC)BrushDlg, (LPARAM)&lb))
  532.     {
  533.         DeleteObject(hbrReal);                      // delete old brush
  534.         logBrush = lb;                              // save changes
  535.         hbrReal = CreateBrushIndirect(&logBrush);   // create new brush
  536.  
  537.         // If "Fill Objects" is turned on, we need to update hbrDraw
  538.         if (GetMenuState(hMenu, IDM_FILL, MF_BYCOMMAND) & MF_CHECKED)
  539.             hbrDraw = hbrReal;
  540.      }
  541.  
  542.     return 0;
  543. }
  544.  
  545.  
  546. //
  547. //  FUNCTION: CmdClear(HWND, WORD, WORD, HWND)
  548. //
  549. //  PURPOSE: Clears the contents of the client window.
  550. //
  551. //  PARAMETERS:
  552. //    hwnd     - The main window.
  553. //    wCommand - IDM_CLEAR (unused)
  554. //    wNotify  - Notification number (unused)
  555. //    hwndCtrl - NULL (unused)
  556. //
  557. //  RETURN VALUE:
  558. //    Always returns 0 - command handled.
  559. //
  560. //  COMMENTS:
  561. //
  562. //
  563.  
  564. #pragma argsused
  565. LRESULT CmdClear(HWND hwnd, WORD wCommand, WORD wNotify, HWND hwndCtrl)
  566. {       
  567.     char szBuffer[9];
  568.     int iResult;
  569.        
  570.     LoadString(hInst, IDS_UNTITLED, szBuffer, sizeof(szBuffer));
  571.               
  572.     // warn the user if important data might be lost
  573.     if (strcmp(szCurrentFile, szBuffer))
  574.      {
  575.         // query the user before blowing old bits away
  576.         iResult = MessageBox(hwnd, 
  577.                              "Rename initialized bitmap as Untitled?", 
  578.                              SZAPPNAME, 
  579.                              MB_YESNOCANCEL);
  580.                              
  581.         switch (iResult)
  582.         {
  583.             case IDYES:
  584.                 // rename the file before continuting, otherwise the original
  585.                 // bitmap file may be inadvertently overwritten on disk
  586.                      strcpy(szCurrentFile, szBuffer);
  587.                 break;
  588.                 
  589.             case IDNO:
  590.                 // allow the operation to continue
  591.                 break;
  592.                 
  593.             case IDCANCEL:
  594.                 // don't erase the drawing surface
  595.                 return 0;
  596.         }        
  597.     }
  598.  
  599.     InitDIBSection(hWndClient);
  600.     
  601.     return 0;
  602. }
  603.  
  604.  
  605. //
  606. //  FUNCTION: MsgClientCreate(HWND, UINT, WPARAM, LPARAM)
  607. //
  608. //  PURPOSE: Handles the WM_CREATE message for the client window.
  609. //
  610. //  PARAMETERS:
  611. //
  612. //    hwnd      - Window handle  (Unused)
  613. //    uMessage  - Message number (Unused)
  614. //    wparam    - Extra data     (Unused)
  615. //    lparam    - Extra data     (Unused)
  616. //
  617. //  RETURN VALUE:
  618. //
  619. //  COMMENTS:
  620. //    Initializes pen and brush objects.
  621. //
  622.  
  623. #pragma argsused
  624. LRESULT MsgClientCreate(HWND   hwnd,
  625.                         UINT   uMessage, 
  626.                         WPARAM wparam, 
  627.                         LPARAM lparam)
  628. {
  629.     // Initialize pens and brushes
  630.  
  631.     hpenBlack = GetStockObject(BLACK_PEN);
  632.      hbrNull   = GetStockObject(NULL_BRUSH);
  633.                 
  634.     InitDrawObjects();            
  635.  
  636.     return 0;
  637. }
  638.  
  639.  
  640. //
  641. //  FUNCTION: InitDrawObjects()
  642. //
  643. //  PURPOSE: Initializes the pen and brush used for drawing.
  644. //
  645. //  PARAMETERS:
  646. //    none
  647. //
  648. //  RETURN VALUE:
  649. //
  650. //  COMMENTS:
  651. //    
  652. //
  653.  
  654. void InitDrawObjects()
  655. {
  656.     hpenDraw  = hpenBlack;
  657.     hbrReal   = GetStockObject(GRAY_BRUSH);
  658.  
  659.     hbrDraw = hbrReal;
  660.  
  661.     GetObject(hpenDraw, sizeof(logPen), &logPen);
  662.     GetObject(hbrReal, sizeof(logBrush), &logBrush);
  663. }
  664.  
  665.  
  666. //
  667. //  FUNCTION: MsgClientDestroy(HWND, UINT, WPARAM, LPARAM)
  668. //
  669. //  PURPOSE: Handles WM_DESTROY message for the client window.
  670. //
  671. //  PARAMETERS:
  672. //
  673. //    hwnd      - Window handle  (Unused)
  674. //    uMessage  - Message number (Unused)
  675. //    wparam    - Extra data     (Unused)
  676. //    lparam    - Extra data     (Unused)
  677. //
  678. //  RETURN VALUE:
  679. //
  680. //    Always returns 0 - Message handled
  681. //
  682. //  COMMENTS:
  683. //    General clean up happens here.
  684. //
  685.  
  686. #pragma argsused
  687. LRESULT MsgClientDestroy(HWND   hwnd,
  688.                                  UINT   uMessage,
  689.                                  WPARAM wparam,
  690.                                  LPARAM lparam)
  691. {
  692.      // Delete the pen and brush used for drawing.  Don't need to delete
  693.      // hpenBlack and hbrNull since these are always stock objects.
  694.  
  695.     DeleteObject(hpenDraw);
  696.     DeleteObject(hbrReal);
  697.  
  698.     return 0;
  699. }
  700.  
  701.  
  702. //
  703. //  FUNCTION: MsgClientLButtonDown(HWND, UINT, WPARAM, LPARAM)
  704. //
  705. //  PURPOSE: Handles WM_LBUTTONDOWN message.
  706. //
  707. //  PARAMETERS:
  708. //
  709. //    hwnd      - Window handle
  710. //    uMessage  - Message number (Unused)
  711. //    wparam    - Extra data     (Unused)
  712. //    lparam    - Mouse coordinates
  713. //
  714. //  RETURN VALUE:
  715. //
  716. //  COMMENTS:
  717. //    Initiates dragging/drawing operation by saving the mouse
  718. //    position and capturing mouse input.
  719. //
  720.  
  721. #pragma argsused
  722. LRESULT MsgClientLButtonDown(HWND   hwnd,
  723.                                       UINT   uMessage,
  724.                              WPARAM wparam,
  725.                              LPARAM lparam)
  726. {
  727.     POINT pt;
  728.  
  729.     // If we aren't already drawing, set focus to the client window
  730.     // so we can get Escape key messages should the user decide to 
  731.     // quit drawing.
  732.  
  733.     if (!bDrawing)
  734.         SetFocus(hwnd);
  735.  
  736.     // Get mouse position
  737.     pt.x = (int)(short)LOWORD(lparam) + cxHorzScrollPos;
  738.     pt.y = (int)(short)HIWORD(lparam) + cyVertScrollPos;
  739.  
  740.     // Call the current LBDown handler
  741.     return (*pfnLBDown)(hwnd, pt);
  742. }
  743.  
  744.  
  745. //
  746. //  FUNCTION: MsgClientMouseMove(HWND, UINT, WPARAM, LPARAM)
  747. //
  748. //  PURPOSE: Handles WM_MOUSEMOVE message.
  749. //
  750. //  PARAMETERS:
  751. //
  752. //    hwnd      - Window handle
  753. //    uMessage  - Message number (Unused)
  754. //    wparam    - Extra data     (Unused)
  755. //    lparam    - Mouse coordinates
  756. //
  757. //  RETURN VALUE:
  758. //
  759. //  COMMENTS:
  760. //    Performs "rubber-banding" by erasing the previous image and
  761. //    redrawing the object using an XOR ROP code.
  762. //
  763.  
  764. #pragma argsused
  765. LRESULT MsgClientMouseMove(HWND   hwnd,
  766.                                     UINT   uMessage,
  767.                            WPARAM wparam,
  768.                            LPARAM lparam)
  769. {
  770.     POINT pt;
  771.     static char szBuf[20] ;     // Array for formatting mouse coordinates
  772.  
  773.     // Get new mouse position
  774.     pt.x = (int)(short)LOWORD(lparam) + cxHorzScrollPos;
  775.     pt.y = (int)(short)HIWORD(lparam) + cyVertScrollPos;
  776.                                       
  777.     // Update the status bar with the new position
  778.     wsprintf(szBuf, "%d,%d", pt.x, pt.y);
  779.     UpdateStatusBar(szBuf, 2, 0);
  780.  
  781.     // Call the current MouseMove handler
  782.     return (*pfnMouseMove)(hwnd, pt);
  783. }
  784.  
  785.  
  786. //
  787. //  FUNCTION: MsgClientLButtonUp(HWND, UINT, WPARAM, LPARAM)
  788. //
  789. //  PURPOSE: Handles WM_LBUTTONUP message.
  790. //
  791. //  PARAMETERS:
  792. //
  793. //    hwnd      - Window handle
  794. //    uMessage  - Message number (Unused)
  795. //    wparam    - Extra data     (Unused)
  796. //    lparam    - Mouse coordinates
  797. //
  798. //  RETURN VALUE:
  799. //
  800. //  COMMENTS:
  801. //    Erases previous rubber-band image and draws the object in
  802. //      the final position.
  803. //
  804.  
  805. #pragma argsused
  806. LRESULT MsgClientLButtonUp(HWND   hwnd,
  807.                                     UINT   uMessage,
  808.                                     WPARAM wparam,
  809.                                     LPARAM lparam)
  810. {
  811.      POINT pt;
  812.  
  813.     // Get new mouse position
  814.     pt.x = (int)(short)LOWORD(lparam) + cxHorzScrollPos;
  815.     pt.y = (int)(short)HIWORD(lparam) + cyVertScrollPos;
  816.   
  817.     // Call the current LBUp handler
  818.     return (*pfnLBUp)(hwnd, pt);
  819. }
  820.  
  821.  
  822. //
  823. //  FUNCTION: MsgClientKeyDown(HWND, UINT, WPARAM, LPARAM)
  824. //
  825. //  PURPOSE: Handles WM_KEYDOWN message.
  826. //
  827. //  PARAMETERS:
  828. //
  829. //    hwnd      - Window handle
  830. //    uMessage  - Message number (Unused)
  831. //    wparam    - Key code (VK_xxx)
  832. //    lparam    - Extra data     (Unused)
  833. //
  834. //  RETURN VALUE:
  835. //
  836. //  COMMENTS:
  837. //    Looks for VK_ESCAPE key only.  If user hits the escape key
  838. //    while drawing, cancel the drawing operation.
  839. //
  840.  
  841. #pragma argsused
  842. LRESULT MsgClientKeyDown(HWND   hwnd,
  843.                                  UINT   uMessage,
  844.                                  WPARAM wparam,
  845.                                  LPARAM lparam)
  846. {
  847.      if (VK_ESCAPE == wparam && bDrawing)
  848.      {
  849.         EndRubberBand(hwnd);
  850.         cPoints = 0;
  851.  
  852.         // get rid of residue from the aborted drawing operation
  853.         InvalidateRect(hwnd, NULL, TRUE);
  854.     }
  855.  
  856.     return 0;
  857. }
  858.  
  859.  
  860. //
  861. //  FUNCTION: xxxLBDown(HWND, UINT, WPARAM, LPARAM)
  862. //  FUNCTION: xxxMouseMove(HWND, UINT, WPARAM, LPARAM)
  863. //  FUNCTION: xxxLBUp(HWND, UINT, WPARAM, LPARAM)
  864. //
  865. //  PURPOSE: Handles WM_LBUTTONDOWN, WM_MOUSEMOVE, and WM_LBUTTONUP
  866. //
  867. //  PARAMETERS:
  868. //
  869. //    hwnd - Window handle
  870. //    pt   - Mouse coordinates
  871. //
  872. //  RETURN VALUE:
  873. //    Always return 0
  874. //
  875. //  COMMENTS:
  876. //    These functions are called by the actual Msgxxx mouse message
  877. //    handler functions (above) to perform input processing.  Three
  878. //    sets of these functions are implemented here.  One to input
  879. //    individual pixels; one to select 2 points (used to draw a
  880. //    line, rectangle, or ellipse); and one to select 4 points (for
  881. //    drawing Bezier curves).  Additional functions could be "plugged
  882. //    in" to support other types of drawing.
  883. //
  884. //    In general, xxxLBDown initializes the drawing process, for example
  885. //    by saving the initial coordinates and calling StartRubberBand;
  886. //    xxxMouseMove updates the coordinates; and xxxLBUp performs any
  887. //    final processing and clean up (EndRubberBand).
  888. //
  889.  
  890. // 
  891. //  FUNCTION: PixelLBDown(HWND, UINT, WPARAM, LPARAM)
  892. //  FUNCTION: PixelMouseMove(HWND, UINT, WPARAM, LPARAM)
  893. //  FUNCTION: PixelLBUp(HWND, UINT, WPARAM, LPARAM)
  894. //
  895. //  PURPOSE: Perform single point selection (individual pixels).
  896. //
  897.  
  898. LRESULT PixelLBDown(HWND hwnd, POINT pt)
  899. {
  900.     // Get a DC and capture mouse input
  901.     StartRubberBand(hwnd);
  902.     SetROP2(hdcRB, R2_COPYPEN);
  903.     SetROP2(hdcMem, R2_COPYPEN);
  904.     
  905.     // Draw the pixel (use current pen color)
  906.     SetPixelV(hdcRB, pt.x, pt.y, logPen.lopnColor);
  907.  
  908.     // do it again in memory DC
  909.     SetPixelV(hdcMem, pt.x, pt.y, logPen.lopnColor);
  910.  
  911.     return 0;
  912. }
  913.  
  914.  
  915. #pragma argsused
  916. LRESULT PixelMouseMove(HWND hwnd, POINT pt)
  917. {
  918.      // If we are currently drawing, draw the pixel
  919.     if (bDrawing)
  920.     {
  921.         SetPixelV(hdcRB, pt.x, pt.y, logPen.lopnColor);
  922.         
  923.          // do it again in memory DC
  924.         SetPixelV(hdcMem, pt.x, pt.y, logPen.lopnColor);
  925.     }
  926.  
  927.      return 0;
  928. }
  929.  
  930.  
  931. #pragma argsused
  932. LRESULT PixelLBUp(HWND hwnd, POINT pt)
  933. {   
  934.     // Don't draw the pixel again here (it's already drawn at LBDown
  935.     // and MouseMove time).  Just need to clean up.
  936.  
  937.     if (bDrawing)
  938.         EndRubberBand(hwnd);
  939.  
  940.      return 0;
  941. }
  942.  
  943.  
  944. // 
  945. //  FUNCTION: BezierLBDown(HWND, UINT, WPARAM, LPARAM)
  946. //  FUNCTION: BezierMouseMove(HWND, UINT, WPARAM, LPARAM)
  947. //  FUNCTION: BezierLBUp(HWND, UINT, WPARAM, LPARAM)
  948. //
  949. //  PURPOSE: Perform 4-point selection for drawing Bezier curves.
  950. //
  951.  
  952. #pragma argsused
  953. LRESULT BezierLBDown(HWND hwnd, POINT pt)
  954. {
  955.      // If this is the first click, initialize rubber banding.
  956.  
  957.      if (0 == cPoints)
  958.           StartRubberBand(hwnd);
  959.  
  960.     return 0;
  961. }
  962.  
  963.  
  964. #pragma argsused
  965. LRESULT BezierMouseMove(HWND hwnd, POINT pt)
  966. {
  967.     int i;
  968.  
  969.     if (!bDrawing || 0 == cPoints)              // Are we currently drawing?
  970.         return 0;
  971.  
  972.     // Are we rubber banding a Bezier?
  973.     if (cPoints >= 2)
  974.         PolyBezier(hdcRB, pPoints, BEZ_MAXPOINTS); // Erase previous Bezier
  975.  
  976.      // Erase previous line segment
  977.     LineDraw(hdcRB, pPoints[cPoints - 1], pPoints[cPoints]);
  978.  
  979.     // Set the rest of the points to be the same as this one
  980.     for (i = cPoints; i < BEZ_MAXPOINTS; i++)
  981.         pPoints[i] = pt;
  982.  
  983.     // Draw new line segment
  984.     LineDraw(hdcRB, pPoints[cPoints - 1], pPoints[cPoints]);
  985.  
  986.     // Are we rubber banding a Bezier?
  987.     if (cPoints >= 2)
  988.           PolyBezier(hdcRB, pPoints, BEZ_MAXPOINTS);
  989.  
  990.     return 0;
  991. }
  992.  
  993.  
  994. LRESULT BezierLBUp(HWND hwnd, POINT pt)
  995. {
  996.     int i;
  997.  
  998.     if (!bDrawing)          // Are we currently drawing?
  999.         return 0;
  1000.  
  1001.     if (cPoints >= 2)       // Are we rubber banding a Bezier?
  1002.     {
  1003.         // Erase previous Bezier
  1004.         PolyBezier(hdcRB, pPoints, BEZ_MAXPOINTS);
  1005.  
  1006.         if (cPoints == BEZ_MAXPOINTS - 1) // Is this the last point?
  1007.         {
  1008.             // If so, then erase all line segments.
  1009.             for (i = BEZ_MAXPOINTS - 1; i > 0; i--)
  1010.                 LineDraw(hdcRB, pPoints[i - 1], pPoints[i]);
  1011.         }
  1012.     }
  1013.     // else do *not* erase the previous line segments
  1014.  
  1015.     // Save new position and increment # of points
  1016.     pPoints[cPoints++] = pt;
  1017.  
  1018.     if (cPoints < BEZ_MAXPOINTS)    // Still more points to get.
  1019.     {
  1020.         // Set the rest of the points to be the same as the current one
  1021.         for (i = cPoints; i < BEZ_MAXPOINTS; i++)
  1022.             pPoints[i] = pt;
  1023.  
  1024.         // Draw new line segment
  1025.         LineDraw(hdcRB, pPoints[cPoints - 1], pPoints[cPoints]);
  1026.  
  1027.         // Once we have at least 2 points let's draw the Bezier curve
  1028.         if (cPoints >= 2)
  1029.             PolyBezier(hdcRB, pPoints, BEZ_MAXPOINTS);
  1030.     }
  1031.     else    // Finish this Bezier and quit drawing.
  1032.     {
  1033.         // Setup DC for 'real' drawing
  1034.         SetROP2(hdcRB, R2_COPYPEN);
  1035.         SetBkMode(hdcRB, TRANSPARENT);
  1036.         SelectObject(hdcRB, hpenDraw);
  1037.  
  1038.         // Draw Bezier in permanent position
  1039.         PolyBezier(hdcRB, pPoints, cPoints);
  1040.  
  1041.         // De-select the pen and clean up rubber banding stuff.
  1042.         SelectObject(hdcRB, hpenBlack);     
  1043.         
  1044.         // do it again in Memory DC         
  1045.         SetROP2(hdcMem, R2_COPYPEN);
  1046.         SetBkMode(hdcMem, TRANSPARENT);
  1047.         SelectObject(hdcMem, hpenDraw);
  1048.  
  1049.         // Draw Bezier in permanent position
  1050.         PolyBezier(hdcMem, pPoints, cPoints);
  1051.  
  1052.         // De-select the pen and clean up rubber banding stuff.
  1053.         SelectObject(hdcMem, hpenBlack);
  1054.         
  1055.         EndRubberBand(hwnd);
  1056.         cPoints = 0;
  1057.     }
  1058.  
  1059.     return 0;
  1060. }
  1061.  
  1062.  
  1063. // 
  1064. //  FUNCTION: RectLBDown(HWND, UINT, WPARAM, LPARAM)
  1065. //  FUNCTION: RectMouseMove(HWND, UINT, WPARAM, LPARAM)
  1066. //  FUNCTION: RectLBUp(HWND, UINT, WPARAM, LPARAM)
  1067. //
  1068. //  PURPOSE: Perform 2-point selection for drawing lines, rectangles,
  1069. //    and ellipses.
  1070. //
  1071.  
  1072. LRESULT RectLBDown(HWND hwnd, POINT pt)
  1073. {
  1074.     // Current position = starting position = mouse position
  1075.     pPoints[0] = pPoints[1] = pt;
  1076.  
  1077.     // Initialize rubber banding
  1078.     StartRubberBand(hwnd);
  1079.  
  1080.     // Draw initial state
  1081.     (*pfnDrawRect)(hdcRB, pPoints[0], pPoints[1]);
  1082.  
  1083.     return 0;
  1084. }
  1085.  
  1086.  
  1087. #pragma argsused
  1088. LRESULT RectMouseMove(HWND hwnd, POINT pt)
  1089. {
  1090.     if (!bDrawing)          // Are we currently drawing?
  1091.         return 0;
  1092.  
  1093.     // Un-draw previous position by redrawing
  1094.     (*pfnDrawRect)(hdcRB, pPoints[0], pPoints[1]);
  1095.  
  1096.     // Save new position
  1097.     pPoints[1] = pt;
  1098.  
  1099.      // Draw in new position
  1100.     (*pfnDrawRect)(hdcRB, pPoints[0], pPoints[1]);
  1101.  
  1102.     return 0;
  1103. }
  1104.  
  1105.  
  1106. LRESULT RectLBUp(HWND hwnd, POINT pt)
  1107. {
  1108.     if (!bDrawing)          // Are we currently drawing?
  1109.         return 0;
  1110.  
  1111.     // Un-draw previous position by redrawing
  1112.     (*pfnDrawRect)(hdcRB, pPoints[0], pPoints[1]);
  1113.  
  1114.     // Save new position
  1115.     pPoints[1] = pt;
  1116.  
  1117.     // Setup DC for 'real' drawing
  1118.     SetROP2(hdcRB, R2_COPYPEN);
  1119.     SetBkMode(hdcRB, TRANSPARENT);
  1120.     SelectObject(hdcRB, hpenDraw);
  1121.     SelectObject(hdcRB, hbrDraw);
  1122.  
  1123.     // Draw object in permanent position
  1124.     (*pfnDrawRect)(hdcRB, pPoints[0], pPoints[1]);
  1125.  
  1126.     // De-select the pen and brush by selecting stock objects 
  1127.     // and clean up rubber banding stuff.
  1128.     SelectObject(hdcRB, hpenBlack);
  1129.     SelectObject(hdcRB, hbrNull); 
  1130.     
  1131.     // draw it again onto bitmap in memory DC   
  1132.     SetROP2(hdcMem, R2_COPYPEN);
  1133.     SetBkMode(hdcMem, TRANSPARENT);
  1134.     SelectObject(hdcMem, hpenDraw);
  1135.     SelectObject(hdcMem, hbrDraw);
  1136.  
  1137.     // Draw object in permanent position
  1138.     (*pfnDrawRect)(hdcMem, pPoints[0], pPoints[1]);
  1139.  
  1140.     // De-select the pen and brush by selecting stock objects 
  1141.     // and clean up rubber banding stuff.
  1142.     SelectObject(hdcMem, hpenBlack);
  1143.     SelectObject(hdcMem, hbrNull);
  1144.     
  1145.     EndRubberBand(hwnd);
  1146.  
  1147.     return 0;
  1148. }
  1149.  
  1150.  
  1151. //
  1152. //  FUNCTION: xxxxDraw(HDC hdc, POINT pt1, POINT pt2)
  1153. //
  1154. //  PURPOSE: Draws a Line, Rectangle, or Ellipse at the coordinates
  1155. //    given by pt1 and pt2.
  1156. //
  1157. //  PARAMETERS:
  1158. //    hdc   - Device context to draw into.
  1159. //    pt1   - Beginning point (upper-left corner of rect)
  1160. //    pt2   - Ending point    (lower-right corner of rect)
  1161. //
  1162. //  RETURN VALUE:
  1163. //    TRUE for success, FALSE otherwise.
  1164. //
  1165. //  COMMENTS:
  1166. //    Assumes the DC is already correctly set up.
  1167. //
  1168. //
  1169.  
  1170. BOOL LineDraw(HDC hdc, POINT pt1, POINT pt2)
  1171. {
  1172.     MoveToEx(hdc, pt1.x, pt1.y, NULL);
  1173.     return LineTo(hdc, pt2.x, pt2.y);
  1174. }
  1175.  
  1176.  
  1177. BOOL RectDraw(HDC hdc, POINT pt1, POINT pt2)
  1178. {
  1179.     return Rectangle(hdc, pt1.x, pt1.y, pt2.x, pt2.y);
  1180. }
  1181.  
  1182.  
  1183. BOOL EllipseDraw(HDC hdc, POINT pt1, POINT pt2)
  1184. {
  1185.     return Ellipse(hdc, pt1.x, pt1.y, pt2.x, pt2.y);
  1186. }
  1187.  
  1188.  
  1189. //
  1190. //  FUNCTION: StartRubberBand(HWND)
  1191. //
  1192. //  PURPOSE: Sets up DC for rubber banding and captures the mouse.
  1193. //
  1194. //  PARAMETERS:
  1195. //    hwnd - Window for GetDC
  1196. //
  1197. //  RETURN VALUE:
  1198. //    None
  1199. //
  1200. //  COMMENTS:
  1201. //
  1202.  
  1203. void StartRubberBand(HWND hwnd)
  1204. {         
  1205.     // Get a DC to draw in
  1206.     hdcRB = GetDC(hwnd);                        
  1207.     
  1208.     // adjust the viewport origin for scrolled view
  1209.     SetViewportOrgEx(hdcRB, -1 * cxHorzScrollPos, -1 * cyVertScrollPos, NULL); 
  1210.  
  1211.     // R2_NOT causes drawing with a pen to invert the screen pixels.
  1212.     // This makes it easy to erase something by drawing it a 2nd time.
  1213.     SetROP2(hdcRB, R2_NOT);
  1214.  
  1215.     // For rubber banding with R2_NOT, we use a single pixel pen and
  1216.     // a NULL brush.
  1217.     SelectObject(hdcRB, hpenBlack);
  1218.     SelectObject(hdcRB, hbrNull);
  1219.  
  1220.     SetCapture(hwnd);                           // Capture mouse input
  1221.     bDrawing = TRUE;     
  1222.     
  1223.     // select the DIB's palette into the screen DC so it will select into the
  1224.     // memory DC properly
  1225.     if (hPalette)
  1226.         hpalRB = SelectPalette(hdcRB, hPalette, TRUE);
  1227.     
  1228.     // setup a memory DC to update the bitmap  
  1229.     // do not adjust its viewport because we are only scrolling the screen DC
  1230.     // i.e., we aren't viewing the image in the memory DC
  1231.     hdcMem = CreateCompatibleDC(hdcRB);      
  1232.     hbmOld = SelectObject(hdcMem, hBitmap);
  1233.  
  1234.     if (hPalette)
  1235.         hpalMem = SelectPalette(hdcMem, hPalette, TRUE);   
  1236. }
  1237.  
  1238.  
  1239. //
  1240. //  FUNCTION: EndRubberBand(HWND)
  1241. //
  1242. //  PURPOSE: Releases rubber banding DC and mouse capture.
  1243. //
  1244. //  PARAMETERS:
  1245. //    hwnd - Window for ReleaseDC
  1246. //
  1247. //  RETURN VALUE:
  1248. //    None
  1249. //
  1250. //  COMMENTS:
  1251. //
  1252.  
  1253. void EndRubberBand(HWND hwnd)
  1254. {   
  1255.     BOOL fOldChanges = fChanges; 
  1256.     
  1257.     if (hpalMem)
  1258.         SelectPalette(hdcMem, hpalMem, FALSE);
  1259.     SelectObject(hdcMem, hbmOld);
  1260.     DeleteDC(hdcMem);
  1261.     fChanges = TRUE;
  1262.     EnableMenuItem(hMenu, IDM_FILESAVE, MF_ENABLED);
  1263.     SendMessage(hWndToolbar, TB_ENABLEBUTTON, IDM_FILESAVE, MAKELONG(TRUE, 0));
  1264.                  
  1265.     if (hpalRB)
  1266.         SelectPalette(hdcRB, hpalRB, FALSE);
  1267.     SetViewportOrgEx(hdcRB, 0, 0, NULL);
  1268.     ReleaseDC(hwnd, hdcRB);
  1269.     ReleaseCapture();    
  1270.     bDrawing = FALSE;
  1271.     
  1272.     if (fOldChanges == FALSE)
  1273.         // need to update main window text now so '*' is included for changes
  1274.         SetWindowTitle(GetParent(hwnd), szCurrentFile);
  1275. }        
  1276.  
  1277.  
  1278. //
  1279. //  FUNCTION: MsgClientPaint(HWND, UINT, WPARAM, LPARAM)
  1280. //
  1281. //  PURPOSE: Paints the client window in response to WM_PAINT message.
  1282. //
  1283. //  PARAMETERS:
  1284. //    hwnd - Handle of Client window.
  1285. //    uMessage - (unused)
  1286. //    wparam - (unused)
  1287. //    lparam - (unused)
  1288. //
  1289. //  RETURN VALUE:
  1290. //    0
  1291. //
  1292. //  COMMENTS:
  1293. //
  1294.  
  1295. #pragma argsused
  1296. LRESULT MsgClientPaint(HWND hwnd, UINT uMessage, WPARAM wparam, LPARAM lparam)
  1297. {
  1298.      HDC         hDC;                 // Handle to DC,
  1299.      PAINTSTRUCT ps;                  // Painting structure
  1300.      RECT        rcClient, rcDDB;     // Client and bitmap rectangles
  1301.      int         xScroll, yScroll;    // Scroll positions
  1302.  
  1303.     // Begin painting
  1304.     hDC = BeginPaint(hwnd, &ps);
  1305.  
  1306.     // if there is a bitmap, display it
  1307.     if (hBitmap)
  1308.     {                      
  1309.         int dx = BitmapWidth(hBitmap);
  1310.         int dy = BitmapHeight(hBitmap);
  1311.     
  1312.         // Get scroll bar positions
  1313.           xScroll  = GetScrollPos(hwnd, SB_HORZ);
  1314.         yScroll  = GetScrollPos(hwnd, SB_VERT);  
  1315.  
  1316.         // Set up the necessary rectangles -- i.e. the rectangle
  1317.         // we're rendering into, and the rectangle in the bitmap
  1318.         GetClientRect(hwnd, &rcClient);
  1319.  
  1320.         rcDDB.left   = xScroll;
  1321.         rcDDB.top    = yScroll;
  1322.         rcDDB.right  = xScroll + RECTWIDTH(&rcClient);
  1323.         rcDDB.bottom = yScroll + RECTHEIGHT(&rcClient);        
  1324.                     
  1325.           if (rcDDB.right > dx)
  1326.         {
  1327.             dx -= rcDDB.right;
  1328.  
  1329.             rcDDB.right     += dx;
  1330.             rcClient.right  += dx;
  1331.         }
  1332.       
  1333.         if (rcDDB.bottom > dy)
  1334.         {
  1335.             dy -= rcDDB.bottom;
  1336.             
  1337.                 rcDDB.bottom    += dy;
  1338.             rcClient.bottom += dy;
  1339.         }
  1340.  
  1341.         // Go do the actual painting.
  1342.         PaintBitmap(hDC, &rcClient, hBitmap, &rcDDB, hPalette);
  1343.     }   
  1344.     else
  1345.     {         
  1346.         // Turn off scroll bars in case they were on
  1347.         SetScrollRange (hwnd, SB_VERT, 0, 0, TRUE);
  1348.         SetScrollRange (hwnd, SB_HORZ, 0, 0, TRUE);         
  1349.           cxHorzScrollPos = cyVertScrollPos = 0;
  1350.     }   
  1351.         
  1352.     EndPaint(hwnd, &ps);
  1353.     return 0;
  1354. }
  1355.  
  1356.  
  1357. //
  1358. //  FUNCTION: MsgClientScroll(HWND, UINT, WPARAM, LPARAM)
  1359. //
  1360. //  PURPOSE: Scrolls the client window in response to scroll message.
  1361. //
  1362. //  PARAMETERS:
  1363. //    hwnd - handle of Client window. 
  1364. //    uMessage - the scroll message.
  1365. //    wParam - contains scroll position and scrollbar type
  1366. //    lParam - (unused)
  1367. //
  1368. //  RETURN VALUE:
  1369. //    None
  1370. //
  1371. //  COMMENTS:
  1372. //    MsgClientScroll performs scrolling in both horiziontal and vertical directions.
  1373. //    If the user clicks on one of the scrolling arrows, the window is scrolled
  1374. //    by (1 / SCROLL_RATIO) of the client area.  For example, if 
  1375. //    SCROLL_RATIO == 4, then the client area is moved over a 1/4 of the 
  1376. //    width or height (as the case may be) of the screen.  If the user pages
  1377. //    up/down the window is scrolled an amount equal to the client area's
  1378. //    width or height as the case may be. If the user moves the thumb to an 
  1379. //    absolute position, the window is scrolled accordingly.
  1380. //
  1381. //
  1382.  
  1383. #define SCROLL_RATIO 4
  1384.  
  1385. #pragma argsused
  1386. LRESULT MsgClientScroll(HWND hwnd, UINT uMessage, WPARAM wparam, LPARAM lparam)
  1387. {
  1388.      int nPos;          // Scrollbar position.
  1389.      int nMin;          // Minumum scroll bar value.
  1390.     int nMax;          // Maximum scroll bar value.
  1391.     int nMove;         // How much to move.
  1392.     int nLine;         // # of pixels for LINEUP/LINEDOWN
  1393.     int nPage;         // # of pixels for PAGEUP/PAGEDOWN
  1394.     int nHorzOrVert;   // SB_HORZ if doing horizontal, SB_VERT if doing vertical
  1395.     RECT rcClient;     // Client area.
  1396.  
  1397.     GetClientRect(hwnd, &rcClient);
  1398.  
  1399.     if (uMessage == WM_HSCROLL)
  1400.     {
  1401.           nHorzOrVert = SB_HORZ;
  1402.         nPage = BitmapWidth(hBitmap) - RECTWIDTH(&rcClient);
  1403.     }
  1404.     else
  1405.     {
  1406.         nHorzOrVert = SB_VERT;
  1407.         nPage = BitmapHeight(hBitmap) - RECTHEIGHT(&rcClient);
  1408.     }
  1409.  
  1410.     if (nPage < 0)
  1411.     {
  1412.         OutputDebugString("Scrolling not necessary\r\n");
  1413.           return 0;
  1414.     }
  1415.  
  1416.     // On a SB_LINEUP/SB_LINEDOWN we will move the picture by
  1417.     //  1 / SCROLL_RATIO of the page amount (i.e. if SCROLL_RATIO
  1418.     //  is 4, it will scroll the picture a quarter of the way)
  1419.  
  1420.     nLine = nPage / SCROLL_RATIO;
  1421.     if (nLine == 0)
  1422.         nLine = 1;
  1423.  
  1424.     nPos = GetScrollPos(hwnd, nHorzOrVert);
  1425.      GetScrollRange(hwnd, nHorzOrVert, &nMin, &nMax);
  1426.  
  1427.     switch (GET_WM_HSCROLL_CODE(wparam, lparam))
  1428.     {
  1429.         case SB_LINEDOWN:             // One line right.
  1430.             nMove = nLine;
  1431.             break;
  1432.  
  1433.         case SB_LINEUP:               // One line left.
  1434.             nMove = -nLine;
  1435.             break;
  1436.  
  1437.           case SB_PAGEDOWN:             // One page right.
  1438.             nMove = nPage;
  1439.             break;
  1440.  
  1441.         case SB_PAGEUP:               // One page left.
  1442.             nMove = -nPage;
  1443.             break;
  1444.  
  1445.         case SB_THUMBPOSITION:        // Absolute position.
  1446.             nMove = GET_WM_HSCROLL_POS(wparam, lparam) - nPos;
  1447.             break;
  1448.  
  1449.           default:                      // No change.
  1450.             nMove = 0;
  1451.             break;
  1452.     }
  1453.  
  1454.     if (nMove)
  1455.     {
  1456.         nPos += nMove;
  1457.  
  1458.         if (nPos < nMin)
  1459.         {
  1460.             nMove -= nPos - nMin;
  1461.                 nPos = nMin;
  1462.         }
  1463.  
  1464.         if (nPos > nMax)
  1465.         {
  1466.             nMove -= nPos - nMax;
  1467.             nPos = nMax;
  1468.         }
  1469.  
  1470.         if (nMove)
  1471.         {
  1472.             SetScrollPos(hwnd, nHorzOrVert, nPos, TRUE);
  1473.  
  1474.             if (nHorzOrVert == SB_HORZ)
  1475.             {
  1476.                 ScrollWindow(hwnd, -nMove, 0, NULL, NULL);
  1477.  
  1478.                 // set the global variable
  1479.                 cxHorzScrollPos = nPos;
  1480.             }
  1481.             else 
  1482.             {
  1483.                 ScrollWindow(hwnd, 0, -nMove, NULL, NULL);
  1484.  
  1485.                      // set the global variable
  1486.                 cyVertScrollPos = nPos;
  1487.             }
  1488.         }
  1489.     }
  1490.     return 0;
  1491. }
  1492.  
  1493. UINT BitmapWidth(HBITMAP hbm)
  1494. {
  1495.     BITMAP bm;
  1496.  
  1497.     if (!hbm)
  1498.         return 0;
  1499.         
  1500.     GetObject(hbm, sizeof(bm), &bm);    
  1501.     return bm.bmWidth;
  1502. }  
  1503.  
  1504. UINT BitmapHeight(HBITMAP hbm)
  1505. {
  1506.     BITMAP bm;
  1507.  
  1508.     if (!hbm)
  1509.         return 0;
  1510.         
  1511.     GetObject(hbm, sizeof(bm), &bm);    
  1512.     return bm.bmHeight;
  1513. }
  1514.